# ~*~ coding: utf-8 ~*~
import time

from flask import current_app
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin, AnonymousUserMixin
from sqlalchemy.dialects.mysql import TINYINT

from apps.common.dbbase import BaseModel
from apps.common.utils import generate_uuid
from apps.extensions import db
from apps.perms.mixin import PermissionMixin
from apps.common.dbtypes import EncryptCharField
from apps.assets.mixin import AssetMixin

__all__ = ['User', 'UserLoginLog', 'PasswordChangeLog']


class MFAMixin:
    otp_level = 0
    otp_secret_key = None

    @property
    def otp_force_enabled(self):
        if current_app.config.get('SECURITY_MFA_AUTH'):
            return True
        return self.otp_level == 2

    def enable_otp(self, otp_secret_key):
        if not self.otp_level == 2:
            self.otp_level = 1
        self.otp_secret_key = otp_secret_key

    def disable_otp(self):
        if not self.otp_level == 2:
            self.otp_level = 0
        self.otp_secret_key = None

    @property
    def otp_turned_on(self):
        if self.otp_level == 1:
            return True
        if self.otp_level == 2 and self.otp_secret_key:
            return True
        return False


class AnonymousUser(AnonymousUserMixin):

    def has_perm(self, perm):
        return False


class User(UserMixin, PermissionMixin, MFAMixin, AssetMixin, BaseModel):
    __tablename__ = 'users_user'

    DEFAULT_PASSWORD = '123456'

    id = db.Column(db.CHAR(36), default=generate_uuid, primary_key=True)
    name = db.Column(db.VARCHAR(128), nullable=False)
    username = db.Column(db.VARCHAR(128), nullable=False, unique=True)
    password = db.Column(db.VARCHAR(512), nullable=False, default=generate_password_hash(DEFAULT_PASSWORD))
    email = db.Column(db.VARCHAR(128), nullable=False, unique=True, default='')
    phone = db.Column(db.CHAR(11), nullable=False, unique=True, default='')
    is_dimission = db.Column(TINYINT, nullable=False, default=1)    # 0: 离职, 1:正常
    is_admin = db.Column(TINYINT, nullable=False, default=0)        # 0: 否,   2:是

    last_login = db.Column(db.INT, nullable=True)
    last_ip = db.Column(db.VARCHAR(128), nullable=True)

    # 0: Disable, 1: Enable, 2: Force enable
    otp_level = db.Column(TINYINT, nullable=False, default=0)
    otp_secret_key = db.Column(EncryptCharField(128), nullable=True, default=None)

    create_time = db.Column(db.INT, nullable=True, default=time.time)
    update_time = db.Column(db.INT, nullable=True, default=time.time, onupdate=time.time)

    def get_password(self):
        return self.password

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

    def __str__(self):
        return "<User:{}>".format(self.username)


class UserLoginLog(BaseModel):
    __tablename__ = 'users_user_login_log'

    MFA_UNKNOWN = -1
    MFA_DISABLED = 0
    MFA_ENABLED = 1
    MFA_FORCE = 2
    MFA_CHOICE = (
        (MFA_UNKNOWN, '-'),
        (MFA_DISABLED, '禁用'),
        (MFA_ENABLED, '启用'),
        (MFA_FORCE, '强制'),
    )

    REASON_NOTHING = 0
    REASON_PASSWORD = 1
    REASON_MFA = 2
    REASON_NOT_EXIST = 3
    REASON_PASSWORD_EXPIRED = 4
    REASON_CHOICE = (
        (REASON_NOTHING, '-'),
        (REASON_PASSWORD, '用户名或密码错误'),
        (REASON_MFA, 'MFA验证失败'),
        (REASON_NOT_EXIST, "用户不存在"),
        (REASON_PASSWORD_EXPIRED, "用户已离职"),
    )

    LOGIN_FAILED = 0
    LOGIN_SUCCESS = 1

    id = db.Column(db.CHAR(36), default=generate_uuid, primary_key=True)
    user = db.Column(db.VARCHAR(128))
    ip = db.Column(db.VARCHAR(128), nullable=True)
    city = db.Column(db.VARCHAR(128), nullable=True)
    mfa = db.Column(TINYINT, default=MFA_UNKNOWN)
    reason = db.Column(TINYINT, default=REASON_NOTHING)
    status = db.Column(TINYINT, default=LOGIN_SUCCESS)

    create_time = db.Column(db.INT, default=time.time)
    update_time = db.Column(db.INT, default=time.time, onupdate=time.time)


class PasswordChangeLog(BaseModel):
    __tablename__ = 'users_password_change_log'

    id = db.Column(db.CHAR(36), default=generate_uuid, primary_key=True)
    user = db.Column(db.VARCHAR(128))
    ip = db.Column(db.VARCHAR(128), nullable=True)
    change_by = db.Column(db.VARCHAR(128))

    create_time = db.Column(db.INT, default=time.time)
    update_time = db.Column(db.INT, default=time.time, onupdate=time.time)

    def __str__(self):
        return "{} change {}'s password".format(self.change_by, self.user)
